package com.baidu.mapapi;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
import android.util.Log;

import androidx.annotation.DrawableRes;

import com.baidu.location.BDAbstractLocationListener;
import com.baidu.location.BDLocation;
import com.baidu.location.LocationClient;
import com.baidu.location.LocationClientOption;
import com.baidu.location.Poi;

import java.util.ArrayList;

/**
 * 百度定位工具类
 */
public class BDLocationHelper {

    @SuppressLint("StaticFieldLeak")
    private static BDLocationHelper myLocation;
    private static final int NOTIFICATION_ID = 1001;

    private ArrayList<Integer> CODES = new ArrayList<>();
    private Activity context;
    private int millisecond;
    private LocationClient mLocClient;
    private LocationCallback callback;

    private BDLocationHelper(Activity context) {
        this.context = context;
        initLocation();
    }

    public static BDLocationHelper getInstance(Activity ctx) {
        if (myLocation == null)
            myLocation = new BDLocationHelper(ctx);
        return myLocation;
    }

    public void addCode(int code) {
        if (!CODES.contains(code)) CODES.add(code);
    }

    public void removeCode(int code) {
        if (CODES.contains(code)) CODES.remove(Integer.valueOf(code));
    }

    public void clearCodes() {
        CODES.clear();
    }

    public BDLocationHelper setDuration(int millisecond) {
        this.millisecond = millisecond;
        if (millisecond >= 1000) {
            //通过setLocOption方法配置选项
            mLocClient.setLocOption(getDefaultOption());
        }
        return this;
    }

    /**
     * 开始定位
     */
    public void startLocation(int code, LocationCallback callback) {
        this.callback = callback;
        addCode(code);
        //开始定位
        if (!mLocClient.isStarted()) {
            mLocClient.start();
        }
    }

    /**
     * 停止定位
     */
    public void stopLocation() {
        if (mLocClient.isStarted()) {
            mLocClient.stop();
            if (millisecond > 0) {
                millisecond = 0;
                //清除
                mLocClient.setLocOption(getDefaultOption());
            }
        }
        myLocation = null;
    }

    public void enableLocInForeground(@DrawableRes int icon, String title, String body) {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationHelper mNotification = new NotificationHelper(context);
            Notification.Builder builder = mNotification.getAndroidChannelNotification(icon, title, body);
            Notification notification = builder.build();
            //noinspection deprecation
            notification.defaults = Notification.DEFAULT_SOUND;
            mLocClient.enableLocInForeground(NOTIFICATION_ID, notification);
        }
    }

    public void disableLocInForeground() {
        mLocClient.disableLocInForeground(true);
    }

    /**
     * 初始化定位
     */
    private void initLocation() {
        //定位初始化
        mLocClient = new LocationClient(context);
        //注册定位监听器
        MyLocationListener mListener = new MyLocationListener();
        mLocClient.registerLocationListener(mListener);
        //通过setLocOption方法配置选项
        mLocClient.setLocOption(getDefaultOption());
    }

    /**
     * 初始化定位参数配置
     */
    private LocationClientOption getDefaultOption() {
        LocationClientOption option = new LocationClientOption();
        //可选，默认高精度，设置定位模式，高精度，低功耗，仅设备
        option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
        //可选，默认gcj02，设置返回的定位结果坐标系，如果配合百度地图使用，建议设置为bd09ll;
        option.setCoorType(BDMapHelper.CoorType_BD09LL);
        //可选，设置是否需要地址信息，默认不需要
        option.setIsNeedAddress(true);
        //可选，设置是否需要地址描述
        option.setIsNeedLocationDescribe(true);
        //可选，设置是否需要设备方向结果
        option.setNeedDeviceDirect(false);
        //可选，默认false，设置是否当gps有效时按照1S1次频率输出GPS结果
        option.setLocationNotify(false);
        //可选，默认true，定位SDK内部是一个SERVICE，并放到了独立进程，设置是否在stop的时候杀死这个进程，默认不杀死
        option.setIgnoreKillProcess(true);
        //可选，默认false，设置是否需要位置语义化结果，可以在BDLocation.getLocationDescribe里得到，结果类似于“在北京天安门附近”
        option.setIsNeedLocationDescribe(true);
        //可选，默认false，设置是否需要POI结果，可以在BDLocation.getPoiList里得到
        option.setIsNeedLocationPoiList(true);
        //可选，默认false，设置是否收集CRASH信息，默认收集
        option.SetIgnoreCacheException(false);
        //可选，默认false，设置是否开启Gps定位
        option.setOpenGps(true);
        //可选，默认false，设置定位时是否需要海拔信息，默认不需要，除基础定位版本都可用
        option.setIsNeedAltitude(false);
        //可选，默认0，即仅定位一次，设置发起连续定位请求的间隔需要大于等于1000ms才是有效的
        if (millisecond > 0) option.setScanSpan(millisecond);
        return option;
    }

    /**
     * 定位回调接口
     */
    public class MyLocationListener extends BDAbstractLocationListener {
        /**
         * 接收异步返回的定位结果，参数是BDLocation类型参数
         */
        @Override
        public void onReceiveLocation(BDLocation location) {
            // 此处的BDLocation为定位结果信息类，通过它的各种get方法可获取定位相关的全部结果
            if (null != location
                    && location.getLocType() != BDLocation.TypeServerError
            ) {
                StringBuilder sb = new StringBuilder(256);
                sb.append("time : ");
                /**
                 * 时间也可以使用systemClock.elapsedRealtime()方法 获取的是自从开机以来，每次回调的时间；
                 * location.getTime() 是指服务端出本次结果的时间，如果位置不发生变化，则时间不变
                 */
                sb.append(location.getTime());
                sb.append("\n定位类型：");
                sb.append(location.getLocType());
                sb.append("\n定位类型说明：");
                sb.append(location.getLocTypeDescription());
                sb.append("\n纬度：");
                sb.append(location.getLatitude());
                sb.append("\n经度：");
                sb.append(location.getLongitude());
                sb.append("\n半径：");
                sb.append(location.getRadius());
                sb.append("\n国家代码：");
                sb.append(location.getCountryCode());
                sb.append("\n国家名称：");
                sb.append(location.getCountry());
                sb.append("\n省：");
                sb.append(location.getProvince());
                sb.append("\n城市编码：");
                sb.append(location.getCityCode());
                sb.append("\n城市：");
                sb.append(location.getCity());
                sb.append("\n区：");
                sb.append(location.getDistrict());
                sb.append("\n街道：");
                sb.append(location.getStreet());
                sb.append("\n地址：");
                sb.append(location.getAddrStr());
                sb.append("\n室内外判断：");
                sb.append(location.getUserIndoorState());
                sb.append("\n方向(不是所有设备都有值)：");
                sb.append(location.getDirection());
                sb.append("\n位置语义化：");
                sb.append(location.getLocationDescribe());
                sb.append("\nPOI信息：");
                if (location.getPoiList() != null && !location.getPoiList().isEmpty()) {
                    for (Poi item : location.getPoiList()) {
                        sb.append(item.getName()).append(";");
                    }
                }
                if (location.getLocType() == BDLocation.TypeGpsLocation) { //GPS定位结果
                    sb.append("\n速度(km/h)：");
                    sb.append(location.getSpeed());
                    sb.append("\n卫星数目：");
                    sb.append(location.getSatelliteNumber());
                    sb.append("\n海拔高度(米)：");
                    sb.append(location.getAltitude());
                    sb.append("\nGps质量判断：");
                    sb.append(location.getGpsAccuracyStatus());
                    sb.append("\n描述：Gps定位成功");

                    if (callback != null) {
                        callback.doWork(location, true, CODES.toArray());
                    }
                } else if (location.getLocType() == BDLocation.TypeNetWorkLocation) { //网络定位结果
                    if (location.hasAltitude()) { //是否有海拔高度
                        sb.append("\n海拔高度(米)：");
                        sb.append(location.getAltitude());
                    }
                    sb.append("\n运营商信息：");
                    sb.append(location.getOperators());
                    sb.append("\n描述：网络定位成功");

                    if (callback != null) {
                        callback.doWork(location, true, CODES.toArray());
                    }
                } else if (location.getLocType() == BDLocation.TypeOffLineLocation) { //离线定位结果
                    sb.append("\n描述：");
                    sb.append("离线定位成功，离线定位结果也是有效的");

                    if (callback != null) {
                        callback.doWork(location, true, CODES.toArray());
                    }
                } else if (location.getLocType() == BDLocation.TypeServerError) {
                    sb.append("\n描述：");
                    sb.append("服务端网络定位失败，可以反馈IMEI号和大体定位时间到loc-bugs@baidu.com，会有人追查原因");

                    if (callback != null) {
                        callback.doWork(location, false, CODES.toArray());
                    }
                } else if (location.getLocType() == BDLocation.TypeNetWorkException) {
                    sb.append("\n描述：");
                    sb.append("网络不同导致定位失败，请检查网络是否通畅");

                    if (callback != null) {
                        callback.doWork(location, false, CODES.toArray());
                    }
                } else if (location.getLocType() == BDLocation.TypeCriteriaException) {
                    sb.append("\n描述：");
                    sb.append("无法获取有效定位依据导致定位失败，一般是由于手机的原因，处于飞行模式下一般会造成这种结果，可以试着重启手机");

                    if (callback != null) {
                        callback.doWork(location, false, CODES.toArray());
                    }
                }

                Log.i("BDLocation", sb.toString());
            } else {
                if (callback != null) {
                    callback.doWork(null, false, CODES.toArray());
                }
            }
        }
    }

    public interface LocationCallback {
        void doWork(BDLocation location, boolean success, Object... codes);
    }

}
